package saer.send.now;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileUpload;

import saer.send.now.channel.Channel;
import saer.send.now.operations.Download;
import saer.send.now.operations.GetId;
import saer.send.now.operations.GetStatus;
import saer.send.now.operations.GetTotalBytes;
import saer.send.now.operations.IOperation;

/**
 * Request example http://sendnow.se/file/<id>/<op>
 * 
 * @author saerd
 * 
 */
@SuppressWarnings("serial")
public class SendNowServlet extends HttpServlet {
    private static final String    BOUNDARY          = "boundary=";
    private static final int       P_OPERATION_INDEX = 2;
    private static final int       P_ID_INDEX        = 3;

    private static final Logger    log               = Logger.getLogger(FileUpload.class.getName());

    static Map<String, IOperation> operations        = new HashMap<String, IOperation>();
    static {
        operations.put(GetId.OP_CODE, new GetId());
        operations.put(GetStatus.OP_CODE, new GetStatus());
        operations.put(GetTotalBytes.OP_CODE, new GetTotalBytes());
        operations.put(Download.OP_CODE, new Download());
    }

    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        log.log(Level.FINEST, "Recived get, {0}", req);
        Map<String, Channel> channelStore = (Map<String, Channel>) getServletContext().getAttribute(Initialize.CHANNEL_STORE);
        if (channelStore == null) {
            log.log(Level.SEVERE, "Channel store is missing");
            resp.setStatus(500);
            return;
        }

        String operationString = getPathElement(req, P_OPERATION_INDEX);
        if (operationString == null) {
            log.log(Level.WARNING, "Missing operation string");
            resp.setStatus(500);
            return;
        }

        IOperation operationObject = operations.get(operationString);
        if (operationObject == null) {
            log.log(Level.WARNING, "Unknown operation, {0}", operationString);
            resp.setStatus(500);
            return;
        }

        operationObject.exec(channelStore, getPathElement(req, P_ID_INDEX), req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        log.log(Level.FINEST, "Recived post, {0}", req);

        Map<String, Channel> channelStore = (Map<String, Channel>) getServletContext().getAttribute(Initialize.CHANNEL_STORE);
        if (channelStore == null) {
            log.log(Level.SEVERE, "Channel store is missing");
            resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return;
        }

        String id = getPathElement(req, P_ID_INDEX);
        if (id == null) {
            log.log(Level.WARNING, "Missing id in file upload request");
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        Channel channel = channelStore.get(id);
        if (channel == null) {
            log.log(Level.WARNING, "Could not find channel for id={0}.", id);
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        String contentLenght = req.getHeader("Content-Length");
        if (contentLenght == null) {
            log.log(Level.WARNING, "Missing header 'Content-Length' for id={0}.", id);
            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        String contentType = req.getHeader("Content-Type");
        if (contentType == null) {
            log.log(Level.WARNING, "Missing header 'Content-Type' for id={0}.", id);
            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        if (!contentType.contains(BOUNDARY) || contentType.split(BOUNDARY).length < 2) {
            log.log(Level.WARNING, "Missing header 'Content-Type' for id={0}.", id);
            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        String separator = contentType.split(BOUNDARY)[1];

        InputStream inputStream = req.getInputStream();
        if (inputStream == null) {
            log.log(Level.WARNING, "Failed to get input stream for id={0}.", id);
            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        channel.upload(inputStream, Integer.parseInt(contentLenght), separator);
        resp.setStatus(HttpServletResponse.SC_OK);

        log.log(Level.FINEST, "Done uploading reqest, {0}, sending responce {1}", new Object[] { req, resp });
    }

    private String getPathElement(HttpServletRequest req, int reverseIndex) {
        String[] parts = req.getRequestURL().toString().split("/");
        if (parts.length < reverseIndex) {
            return null;
        }
        return parts[parts.length - reverseIndex];
    }
}
